package com.xtsoft.platform.base.system.service.impl;

import com.alibaba.druid.util.StringUtils;
import com.xtsoft.platform.base.system.domain.SysResource;
import com.xtsoft.platform.base.system.domain.SysRole;
import com.xtsoft.platform.base.system.repository.ResourceRoleRepository;
import com.xtsoft.platform.base.system.repository.SysResourceRepository;
import com.xtsoft.platform.base.system.repository.SysRoleRepository;
import com.xtsoft.platform.globe.enums.YesNo;
import jakarta.annotation.PostConstruct;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;
import com.xtsoft.platform.base.system.domain.ResourceRole;
import java.util.*;

@Service
public class InvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {
    private static final Logger logger = LoggerFactory.getLogger(InvocationSecurityMetadataSourceService.class);

    private static String LOCAL_UNDERLINE = "__";



    /**
     * 当访问未定义权限控制的资源时，允许或拒绝
     */
    private boolean rejectPublicInvocations = false;


    @Autowired
    private SysRoleRepository sysRoleRepository;


    @Autowired
    private SysResourceRepository sysResourceRepository;

    @Autowired
    private ResourceRoleRepository resourceRoleRepository;

    /*
     * 启动时装载资源
     * 注：
     * 1、HashMap<key-value>，其中的key是不可以重复的，这点要注意
     * 重复的key会覆盖原先的KEY，因此选用HASHMAP，必须采用合成唯一KEY
     * 目前采用ROLE + RESOURCE的形式合成唯一KEY，即
     * Alias  + "__"  + URL的形式，Value为ROLE别名
     *
     * 2、在单组织中，必须保证ROLE别名唯一，不然必然产生重复KEY
     *
     * 3、若采用多组织模型，即组织与角色为一对多关系，则合成KEY应以
     * ORGANIZE  + ROLE + RESOURCE
     *
     * 4、当资源被编辑时，应重新装载资源，否则只能等到重启应用时，
     * 编辑结果才会生效
     *
     */
    private Map<String, Collection<ConfigAttribute>> resourceMap = null;

    private List<ConfigAttribute>  roles = null;

    @PostConstruct
    public  void loadResourceDefine() {
        this.resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
        this.roles = new ArrayList<ConfigAttribute>();//先new一下两个集合
        //获取用户角色集合
        List<SysRole> sysRoles= sysRoleRepository.findByState(YesNo.Yes.getIntValue());
        //开始for循环
        for (SysRole sysRole : sysRoles) {
            Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
        //获取role包含的资源
            List<ResourceRole> resourceRoles = resourceRoleRepository.findBySysRole(sysRole);
            if(resourceRoles.size() < 1) {
                //该角色未分配权限
                continue;
                //如果这个不存咋，直接跳过循环
            }
            ConfigAttribute ca = new SecurityConfig(sysRole.getAlias());
            atts.add(ca);
            this.roles.add(ca);
            //获取系统资源装入集合中
            List<SysResource> sysResources = new ArrayList();
            resourceRoles.forEach(action->{
                if(null != action.getSysResource()) {
                    sysResources.add(action.getSysResource());
                }
            });
            //逐个遍历系统资源的url，将url分割成多个子串，将角色别名、资源路径和权限配置（atts）的关联信息存储到 resourceMap 中。这里使用角色别名和资源路径拼接成的字符串作为键，将对应的权限配置 atts 存储起来。
            for (SysResource sysResource : sysResources) {
                String url=sysResource.getResourceUrl();
                if(!StringUtils.isEmpty(url)){
                    String[] urls = url.split(";");//将url字串用;方式分割
                    for(String urlStr: urls)
                        this.resourceMap.put(sysRole.getAlias() + LOCAL_UNDERLINE + urlStr, atts);
                }
            }

        }}

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        return List.of();
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return List.of();
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return false;
    }
}
